import { computed } from 'vue'
import { useStore, mapState, mapGetters } from 'vuex'
// mapActions, mapMutations 组件中可以直接使用原生写法，不需要封装
 
 
/**
 * 重新封装 mapState，使用方法与返回结果跟官方原生 mapState 一致，适用全局和 modules 局部
 * 推荐使用数组形式获取，写法更简洁
 */
const MAPSTATE = (...args) => {
    const $store = useStore()
    const methodName = 'MAPSTATE'
    const target = 'state'
    let modulesName = null
    let params = null
    let storeState = {}
 
    // 检测参数在 state 中是否存在
    const paramsCheckFn = (paramsValusArgs, $storeState) => {
        paramsValusArgs.forEach(item => {
            if (!Object.keys($storeState).includes(item)) {
                throw new Error(`${methodName}方法里的参数"${item}" Vuex ${target} 中不存在`)
            }
        })
    }
 
    // 调用 mapState 的参数为对象
    const useObjectTypeFn = (params, $storeState) => {
        if (Object.prototype.toString.call(params) === '[object Object]') {
            const paramsValusArgs = Object.values(params)
            paramsCheckFn(paramsValusArgs, $storeState)
        }
    }
 
    // 调用 mapState 的参数为数组
    const useArrayTypeFn = (params, $storeState) => {
        if (Array.isArray(params)) {
            if (!params.length) throw new Error('${methodName}方法的数组参数长度不能为0')
            paramsCheckFn(params, $storeState)
        }
    }
 
    // computed 优化，给 mapState 返回的每个函数的 this 绑定 {$store} 对象
    const improveComputedFn = storeStateFnsObj => {
        Object.keys(storeStateFnsObj).forEach(key => {
            const fn = storeStateFnsObj[key].bind({$store})
            storeState[key] = computed(fn)
        })
    }
 
    if (args && args.length === 1) {
        // 用户调用全局 Vuex
        params = args[0]
        useObjectTypeFn(params, $store.state)
        useArrayTypeFn(params, $store.state)
        improveComputedFn(mapState(params))
    } else if (args && args.length === 2) {
        // 用户调用局部模块化 Vuex
        modulesName = args[0]
        params = args[1]
 
        useObjectTypeFn(params, $store.state[modulesName])
        useArrayTypeFn(params, $store.state[modulesName])
        improveComputedFn(mapState(modulesName, params))
    }
    return storeState
}
 
 
/**
 * 重新封装 mapGetters，使用方法与返回结果跟官方原生 mapGetters 一致，适用全局和 modules 局部
 * 推荐使用数组形式获取，写法更简洁
 */
const MAPGETTERS = (...args) => {
    const $store = useStore()
    const methodName = 'MAPGETTERS'
    const target = 'getters'
    let modulesName = null
    let params = null
    const storeGetters = {}
 
    // 检测参数在 getters 中是否存在
    const paramsCheckFn = (paramsValusArgs, isModulesName) => {
        // 检测参数在 getters 中是否存在
        paramsValusArgs.forEach(item => {
            if (isModulesName) {
                item =  `${modulesName}/${item}`
            } 
            if (!Object.keys($store.getters).includes(item)) {
                throw new Error(`${methodName}方法里的参数"${item}" Vuex ${target} 中不存在`)
            }
        })
    }
 
    // 调用 mapGetters 的参数为对象
    const useObjectTypeFn = (params, modulesName) => {
        if (Object.prototype.toString.call(params) === '[object Object]') {
            const paramsValusArgs = Object.values(params)
            paramsCheckFn(paramsValusArgs, modulesName)
        }
    }
 
    // 调用 mapGetters 的参数为数组
    const useArrayTypeFn = (params, modulesName) => {
        if (Array.isArray(params)) {
            if (!params.length) throw new Error('${methodName}方法的数组参数长度不能为0')
            paramsCheckFn(params, modulesName)
        }
    }
 
    // computed 优化，给 mapGetters 返回的每个函数的 this 绑定 {$store} 对象
    const improveComputedFn = storeGettersFnsObj => {
        Object.keys(storeGettersFnsObj).forEach(key => {
            const fn = storeGettersFnsObj[key].bind({$store})
            storeGetters[key] = computed(fn)
        })
    }
 
    if (args && args.length === 1) {
        // 用户调用全局 Vuex
        params = args[0]
        useObjectTypeFn(params)
        useArrayTypeFn(params)
        improveComputedFn(mapGetters(params))
    } else if (args && args.length === 2) {
        // 用户调用局部模块化 Vuex
        modulesName = args[0]
        params = args[1]
        useObjectTypeFn(params, modulesName)
        useArrayTypeFn(params, modulesName)
        improveComputedFn(mapGetters(modulesName, params))
    }
    return storeGetters
}
 
export { MAPSTATE, MAPGETTERS }